package com.cordys.opensource.crom;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.cordys.cpc.bsf.busobject.BSF;
import com.cordys.cpc.bsf.busobject.BusObject;
import com.cordys.cpc.bsf.classinfo.ClassInfo;
import com.cordys.cpc.bsf.event.AfterCommitObjectEvent;
import com.cordys.cpc.bsf.event.BeforeCommitObjectEvent;
import com.cordys.cpc.bsf.event.BusObjectEvent;
import com.cordys.cpc.bsf.event.BusObjectUpdateEvent;
import com.cordys.cpc.bsf.event.IAfterCommitObjectListener;
import com.cordys.cpc.bsf.event.IBeforeCommitObjectListener;
import com.cordys.cpc.bsf.event.IBeforeDeleteListener;
import com.cordys.cpc.bsf.event.IBeforeInsertListener;
import com.cordys.cpc.bsf.event.IBeforeUpdateListener;

/**
 * DBObjectEventListener is a class that listens to events on the BusObjects in the DB package configured in the crom connector.
 * By default this class will write values in the columns CREATED_BY and CREATED_ON if they are available in the database object.
 * Additionally, this class is a generic hook on which listeners can subscribe to see all WS-AppServer events on all database classes, without
 * the need to register listeners on each individual database class.
 */
public class DBObjectEventListener implements IBeforeInsertListener, IBeforeUpdateListener, IBeforeDeleteListener, IBeforeCommitObjectListener, IAfterCommitObjectListener
{
	private final ClassInfo dbClassInfo;
	
	private static final List<IBeforeInsertListener> beforeInsertListeners = new ArrayList<IBeforeInsertListener>();
	
	/**
	 * Registers a listener that will be fired before a DB object is inserted.
	 * @param listener
	 */
	public static void addBeforeInsertListener(IBeforeInsertListener listener)
	{
		beforeInsertListeners.add(listener);
	}
	
	/**
	 * Removes an earlier registered listener
	 * @param listener
	 */
	public static void removeBeforeInsertListener(IBeforeInsertListener listener)
	{
		beforeInsertListeners.remove(listener);
	}
	
	private static final List<IBeforeUpdateListener> beforeUpdateListeners = new ArrayList<IBeforeUpdateListener>();

	/**
	 * Registers a listener that will be fired before a DB object is updated.
	 * @param listener
	 */
	public static void addBeforeUpdateListener(IBeforeUpdateListener listener)
	{
		beforeUpdateListeners.add(listener);
	}
	
	/**
	 * Removes an earlier registered listener
	 * @param listener
	 */
	public static void removeBeforeUpdateListener(IBeforeUpdateListener listener)
	{
		beforeUpdateListeners.remove(listener);
	}
	
	private static final List<IBeforeDeleteListener> beforeDeleteListeners = new ArrayList<IBeforeDeleteListener>();

	/**
	 * Registers a listener that will be fired before a DB object is deleted.
	 * @param listener
	 */
	public static void addBeforeDeleteListener(IBeforeDeleteListener listener)
	{
		beforeDeleteListeners.add(listener);
	}
	
	/**
	 * Removes an earlier registered listener
	 * @param listener
	 */
	public static void removeBeforeDeleteListener(IBeforeDeleteListener listener)
	{
		beforeDeleteListeners.remove(listener);
	}
	
	private static final List<IBeforeCommitObjectListener> beforeCommitListeners = new ArrayList<IBeforeCommitObjectListener>();

	/**
	 * Registers a listener that will be fired before a DB object is committed.
	 * @param listener
	 */
	public static void addBeforeCommitListener(IBeforeCommitObjectListener listener)
	{
		beforeCommitListeners.add(listener);
	}
	
	/**
	 * Removes an earlier registered listener
	 * @param listener
	 */
	public static void removeBeforeCommitListener(IBeforeCommitObjectListener listener)
	{
		beforeCommitListeners.remove(listener);
	}
	
	private static final List<IAfterCommitObjectListener> afterCommitListeners = new ArrayList<IAfterCommitObjectListener>();

	/**
	 * Registers a listener that will be fired after a DB object is committed.
	 * @param listener
	 */
	public static void addAfterCommitListener(IAfterCommitObjectListener listener)
	{
		afterCommitListeners.add(listener);
	}
	
	/**
	 * Removes an earlier registered listener
	 * @param listener
	 */
	public static void removeAfterCommitListener(IAfterCommitObjectListener listener)
	{
		afterCommitListeners.remove(listener);
	}
	
	/**
	 * Creates a new DBObjectEventListener. Internal method to be used by crom only.
	 */
	DBObjectEventListener(Class<? extends BusObject> dbClass)
	{
		dbClassInfo = BusObject._getClassInfo(dbClass);

		// Only take those classes for which class info belongs to it.
		//  E.g., generated base classes have the same ClassInfo object as their extension class,
		//  but the base classes should not be registered.
		if (dbClassInfo==null || !dbClassInfo.getDefinedClass().equals(dbClass))
		{
			return;
		}
		
		dbClassInfo.addBeforeInsertListener(this);
		dbClassInfo.addBeforeUpdateListener(this);
		dbClassInfo.addBeforeDeleteListener(this);
		dbClassInfo.addBeforeCommitListeners(this);
		dbClassInfo.addAfterCommitListener(this);
	}

	@Override
	public void onBeforeInsert(BusObjectEvent boe)
	{
		BusObject busObject = boe.getObject();
		
		// Set "CREATED_BY" if available as attribute and 
		//  not yet done (it can happen in some higher level logic, e.g. during creation of new revisions in DomainObjectEventListener)
		if (dbClassInfo.getAttributeInfo(RevisionedObject.CREATED_BY)!=null && busObject.isNull(RevisionedObject.CREATED_BY))
		{
			busObject.setProperty(RevisionedObject.CREATED_BY, BSF.getUser(), 0);
		}

		// Set "CREATED_ON" if available as attribute and 
		//  not yet done (it can happen in some higher level logic, e.g. during creation of new revisions in DomainObjectEventListener)
		if (dbClassInfo.getAttributeInfo(RevisionedObject.CREATED_ON)!=null && busObject.isNull(RevisionedObject.CREATED_ON))
		{
			busObject.setProperty(RevisionedObject.CREATED_ON, new Date(), 0);
		}
		
		for (IBeforeInsertListener listener : beforeInsertListeners)
		{
			listener.onBeforeInsert(boe);
		}
	}

	@Override
	public void onBeforeUpdate(BusObjectUpdateEvent boe)
	{
		// Useless statements allowing for easy breakpoints
		BusObject busObject = boe.getObject();
		if (busObject!=null) busObject = null;
		
		for (IBeforeUpdateListener listener : beforeUpdateListeners)
		{
			listener.onBeforeUpdate(boe);
		}
	}

	@Override
	public void onBeforeDelete(BusObjectEvent boe)
	{
		// Useless statements allowing for easy breakpoints
		BusObject busObject = boe.getObject();
		if (busObject!=null) busObject = null;
		
		for (IBeforeDeleteListener listener : beforeDeleteListeners)
		{
			listener.onBeforeDelete(boe);
		}
	}

	@Override
	public void onBeforeCommit(BeforeCommitObjectEvent boe)
	{
		// Useless statements allowing for easy breakpoints
		BusObject busObject = boe.getObject();
		if (busObject!=null) busObject = null;
		
		for (IBeforeCommitObjectListener listener : beforeCommitListeners)
		{
			listener.onBeforeCommit(boe);
		}
	}

	@Override
	public void onAfterCommit(AfterCommitObjectEvent boe)
	{
		// Useless statements allowing for easy breakpoints
		BusObject busObject = boe.getObject();
		if (busObject!=null) busObject = null;
		
		for (IAfterCommitObjectListener listener : afterCommitListeners)
		{
			listener.onAfterCommit(boe);
		}
	}

}
